﻿// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Text;           // Used for StringBuilder class
using System.IO;             // Used for TextWriter and StreamWriter class.
using System;                // Used for Exception class.
using System.Windows.Forms;  // Used for MessageBox class


namespace TraceabilityTool
{
    class ReportWriter
    {
        // This class is responsible for writing reports generated by ReportGenerator class to plain text files.
        public static void WriteTraceabilityReport(string outputFolderPath)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string key in ReportGenerator.reqDocLookup.Keys)
            {
                // Print requirement ID.
                sb.AppendLine(key);

                // Print the path of the document where the requirement was defined.
                sb.AppendLine("     Defined in:  " + ReportGenerator.reqDocLookup[key]);

                // Print source code file paths
                bool firstLine = true;
                if (ReportGenerator.reqCodeMatrix.ContainsKey(key))
                {
                    foreach (FilePathLineNum reqData in ReportGenerator.reqCodeMatrix[key])
                    {
                        if (firstLine)
                        {
                            sb.AppendLine("     Coded in:    " + reqData.filePath + ", line " + reqData.lineNum.ToString());
                            firstLine = false;
                        }
                        else
                        {
                            sb.AppendLine("                  " + reqData.filePath + ", line " + reqData.lineNum.ToString());
                        }
                    }
                }
                else
                {
                    sb.AppendLine("     Not Coded");
                }

                // Print test code file paths
                if (ReportGenerator.reqTestMatrix.ContainsKey(key))
                {
                    firstLine = true;
                    foreach (FilePathLineNum reqData in ReportGenerator.reqTestMatrix[key])
                    {
                        if (firstLine)
                        {
                            sb.AppendLine("     Tested in:   " + reqData.filePath + ", line " + reqData.lineNum.ToString());
                            firstLine = false;
                        }
                        else
                        {
                            sb.AppendLine("                  " + reqData.filePath + ", line " + reqData.lineNum.ToString());
                        }
                    }
                }
                else
                {
                    sb.AppendLine("     Not Tested");
                }
            }

            // Print totals
            sb.AppendLine("".PadRight(42, '='));
            sb.AppendLine("Total unique requirements       :  " + ReportGenerator.reqDocLookup.Count.ToString());
            sb.AppendLine("Total implemented requirements  :  " + ReportGenerator.reqCodeMatrix.Count.ToString());
            sb.AppendLine("Total tested requirements       :  " + ReportGenerator.reqTestMatrix.Count.ToString());
            sb.AppendLine("Total unimplemented requirements:  " + ReportGenerator.missingCodeCoverage.Count.ToString());
            sb.AppendLine("Total untested requirements     :  " + ReportGenerator.missingTestCoverage.Count.ToString());

            // Output data to a CSV file.
            writeStringToFile(sb.ToString(), outputFolderPath + @"\traceability_matrix.txt");

        }


        public static void WriteInvalidReqReport(string outputFolderPath)
        {
            int maxKeyWidth = ReportGenerator.invalidRequirements.maxKeyLength + 3;
            int maxReasonWidth = ReportGenerator.invalidRequirements.maxReasonLength + 3;
            StringBuilder sb = new StringBuilder();

            foreach (string key in ReportGenerator.invalidRequirements.Keys)
            {
                sb.Append(key.PadRight(maxKeyWidth));
                bool newLine = false;
                foreach (InvalidReqDictEntry entry in ReportGenerator.invalidRequirements[key])
                {
                    if (newLine)
                    {
                        sb.AppendLine("".PadRight(maxKeyWidth) + entry.reason.PadRight(maxReasonWidth) + entry.filePath + ", line " + entry.lineNum.ToString());
                    }
                    else
                    {
                        sb.AppendLine(entry.reason.PadRight(maxReasonWidth) + entry.filePath + ", line " + entry.lineNum.ToString());
                        newLine = true;
                    }
                }
            }
            sb.AppendLine("".PadRight(35, '='));
            sb.AppendLine("Total invalid requirements:  " + ReportGenerator.invalidRequirements.Count.ToString());

            // Output data to a CSV file.
            writeStringToFile(sb.ToString(), outputFolderPath + @"\invalid_requirements.txt");

        }


        public static void WriteMissingCodeCoverageReport(string outputFolderPath)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string key in ReportGenerator.missingCodeCoverage.Keys)
            {
                sb.AppendLine(key.PadRight(ReportGenerator.missingCodeCoverageKeyWidth + 3) + ReportGenerator.missingCodeCoverage[key]);
            }
            sb.AppendLine("".PadRight(41, '='));
            sb.AppendLine("Total unimplemented requirements:  " + ReportGenerator.missingCodeCoverage.Count.ToString());

            // Output data to a CSV file.
            writeStringToFile(sb.ToString(), outputFolderPath + @"\missing_code_coverage.txt");
        }


        public static void WriteMissingTestCoverageReport(string outputFolderPath)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string key in ReportGenerator.missingTestCoverage.Keys)
            {
                sb.AppendLine(key.PadRight(ReportGenerator.missingTestCoverageKeyWidth + 3) + ReportGenerator.missingTestCoverage[key]);
            }
            sb.AppendLine("".PadRight(35, '='));
            sb.AppendLine("Total untested requirements:  " + ReportGenerator.missingTestCoverage.Count.ToString());

            // Output data to a CSV file.
            writeStringToFile(sb.ToString(), outputFolderPath + @"\missing_test_coverage.txt");
        }


        public static void WriteMissingCodeAndTestCoverageReport(string outputFolderPath)
        {
            int count = 0;
            int maxKeyWidth = ReportGenerator.missingTestCoverageKeyWidth;
            StringBuilder sb = new StringBuilder();

            // Pick the shorter of the key lengths from missing code and test coverage lists
            // (if a longer key is not found on both lists at the same time, it will not be output to the report).
            if (ReportGenerator.missingCodeCoverageKeyWidth < maxKeyWidth)
                maxKeyWidth = ReportGenerator.missingCodeCoverageKeyWidth;

            foreach (string key in ReportGenerator.missingTestCoverage.Keys)
            {
                if (ReportGenerator.missingCodeCoverage.ContainsKey(key))
                {
                    sb.AppendLine(key.PadRight(maxKeyWidth + 3) + ReportGenerator.missingTestCoverage[key]);
                    count++;
                }
            }
            sb.AppendLine("".PadRight(75, '='));
            sb.AppendLine("Total number of requirements missing both implementation and tests:  " + count.ToString());

            // Output data to a CSV file.
            writeStringToFile(sb.ToString(), outputFolderPath + @"\missing_code_and_test_coverage.txt");
        }


        public static void WriteRepeatingReqReport(string outputFolderPath)
        {
            StringBuilder sb = new StringBuilder();
            foreach (string key in ReportGenerator.repeatingRequirements.Keys)
            {
                sb.Append(key.PadRight(ReportGenerator.repeatingRequirementsKeyWidth + 3));
                bool newLine = false;
                foreach (string reqDocPath in ReportGenerator.repeatingRequirements[key])
                {
                    if (newLine)
                    {
                        sb.AppendLine("".PadRight(ReportGenerator.repeatingRequirementsKeyWidth + 3) + reqDocPath);
                    }
                    else
                    {
                        sb.AppendLine(reqDocPath);
                        newLine = true;
                    }

                }
            }
            sb.AppendLine("".PadRight(35, '='));
            sb.AppendLine("Total repeating requirements:  " + ReportGenerator.repeatingRequirements.Count.ToString());

            // Output data to a CSV file.
            writeStringToFile(sb.ToString(), outputFolderPath + @"\repeating_requirements.txt");
        }



        public static void writeStringToFile(string str, string outputFile)
        {
            try
            {
                TextWriter outfile = new StreamWriter(outputFile);
                {
                    outfile.Write(str);
                    outfile.Close();
                }
            }
            catch (Exception exception)
            {
                string message = "An error occurred while attempting to access the file " + outputFile + System.Environment.NewLine +
                                    exception.Message + System.Environment.NewLine;
                if (ReportGenerator.useGUI)
                    MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                else
                    Console.WriteLine(message);
                Program.exitCode = 1;
            }
        }

    }
}
